package com.hccl.service.parser;

import com.hccl.exception.updateException;
import com.hccl.exception.updateFileOperateException;
import com.hccl.mongoEntities.ParserParamEntity;
import com.hccl.service.update.UpdateProcesses;
import com.hccl.util.FileOperations;
import entities.SLUResult;
import org.mongodb.morphia.Datastore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import static com.hccl.config.Constants.*;

/**
 * 理解引擎的功能类，封装线程池对象并实现理解和语法更新接口的主要流程
 */
@Service
public class ParserService {
    @Autowired
    ParserPool parserPool;

    @Autowired
    Datastore datastore;

    @Autowired
    UpdateProcesses updateProcesses;
    private static final  Logger LOG = LoggerFactory.getLogger(ParserService.class);

    /**
     * description: 理解引擎的入口方法
     * @Param: url请求参数对象
     * @return 导航引擎的结果：Map包括语义理解结果，语义理解结果里的词典信息和对话提供的当前的Action信息
     */
    public Map<String,Object> sluParser(ParserParamEntity parserParam) throws Exception {

        //处理新增参数
        Map<String,String> newAddParam = new HashMap();
        newAddParam.put("inter_idx",parserParam.getInter_idx());
        newAddParam.put("start_time",parserParam.getStart_time());
        //ArrayList<SLUResult> originsSLU
        Map<String,Object> resultMap = new HashMap();
        Parser parser = parserPool.take();
        ArrayList<SLUResult> originsSLU = new ArrayList<SLUResult>();
        if (parserParam.getGreeting() != null) {
            parser.greeting(parserParam.getUserid(), parserParam.getTelephoneNumber(), newAddParam);
        } else if (parserParam.getKeypad() != null) {
            parser.keypadParser(parserParam.getKeypad(), parserParam.getUserid(), parserParam.getTelephoneNumber(), newAddParam);
        } else {
            parser.queryParser(parserParam.getLine(), parserParam.getUserid(), parserParam.getTelephoneNumber(), newAddParam);
            //originsSLU = parser.queryParser1(parserParam.getLine());
        }
        resultMap.put("currentSLU", parser.getCurrentSLU());
        resultMap.put("currentAction",parser.getCurrentAction());
        resultMap.put("currentGaz",parser.getCurrentGaz());
        //resultMap.put("originSLU",originsSLU);
        updatekey(parser);
        return resultMap;
    }

    /**
     * description: 更新线程池中待调用的parser对象
     * @Param: parser对象
     * @return null
     */
    public  void updatekey(Parser parser){
        if (!isUpdating() & !updateProcesses.isUpdating()){
            try {
                parserPool.add(parser);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return;
        }else if (updateProcesses.isUpdating() & !updateProcesses.isUpdateProcess()){
            updateProcesses.updateParser(parser);
            return;
        }else if (updateProcesses.isUpdating() & updateProcesses.isUpdateProcess()){
            try {
                parserPool.add(parser);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return;
        }
        //为了保证初始化成功
        try {
            initlock.lock();
            if (parserMap.containsKey(parser.getId())){
                parserPool.add(parserMap.get(parser.getId()));
                parser.cleanupSerialController();
                System.out.println("更新编号为"+parser.getId()+"的Parser");
                parserMap.remove(parser.getId());
            }
            if (parserMap.size()==0){
                //表示更新完成
                try {
                    singallocker.lock();
                    condition.signal();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    singallocker.unlock();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            initlock.unlock();
        }
    }


    public void dmReset(ParserParamEntity parserParam){
        parserPool.getDialogManager().resetState(parserParam.getUserid());
    }

    //=======================================UPDATE====================================//
    /**
     * description: 解决同一时间只允许一个更新
     * @Param: null
     * @return 是否允许当前更新，若当前存在更新则返回false
     */
    public boolean update() throws updateException {
        try {
            if (isUpdating()) {
                System.out.println("同一时刻只允许一个更新");
                return false;
            }
            return updateAction();
        }catch (Exception e){
            throw new updateException("更新失败。系统尝试自动恢复...");
        }
    }

    //第一把锁，为了保护update同一时刻只执行一次
    private final static ReentrantLock locker = new ReentrantLock(false);

    //第二把锁,为了等待更新完成
    private final static ReentrantLock singallocker = new ReentrantLock(false);
    private final static Condition condition=singallocker.newCondition();

    //第三把锁，为了初始化map
    private final static ReentrantLock initlock = new ReentrantLock(false);

    //线程安全的map
    public ConcurrentMap<Integer, Parser> parserMap = new ConcurrentHashMap<>();
    private boolean isUpdating(){
        return locker.isLocked();
    }

    /**
     * description:更新线程池中待调用的Parser对象
     * @Param: null
     * @return null
     */
    private  boolean updateAction() throws updateException {
        try {
            locker.lock();
            //增加一个初始化的读写锁
            try {
                initlock.lock();
                List<Parser> newParserList = parserPool.getNewParserList();
                for (Parser newParser:
                        newParserList) {
                    parserMap.put(newParser.getId(),newParser);
                }
                List<Parser> oldParserList = new ArrayList<Parser>();
                parserPool.getPool().drainTo(oldParserList);
                for (Parser oldparser:oldParserList){
                    parserPool.getPool().add(parserMap.get(oldparser.getId()));
                    System.out.println("更新编号为"+oldparser.getId()+"的Parser");
                    oldparser.cleanupSerialController();
                    parserMap.remove(oldparser.getId());
                }
            }catch (Exception e){//更新失败，抛出异常
                throw new updateException("更新时updateAction()出错。系统尝试自动恢复...");
            }finally {
                initlock.unlock();
            }
            //等待更新完成
            if (!parserMap.isEmpty()){
                try {
                    singallocker.lock();
                    condition.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                finally {
                    singallocker.unlock();
                }
            }
        } catch (Exception e) {
            throw e;
        }
        finally {
            locker.unlock();
        }
        return true;
    }


    /**
     * description: 更新前的文件备份
     * @Param: null
     * @return null
     */
    public void backupFiles() throws updateFileOperateException {
        //备份完毕
        FileOperations.reNameFile(gateJapePath);
        FileOperations.reNameFile(gateGazetteerPath);
        FileOperations.reNameFile(corpusPath);
        FileOperations.reNameFile(nodeInfoPath);
        FileOperations.reNameFile(classifierModelPath);
        FileOperations.reNameFile(classifierTraindataPath);

    }

    /**
     * description: 恢复原始corpus,nodeInfo,model.bin,traindata
     * @Param: null
     * @return null
     */
    public void recovery() throws updateFileOperateException {
        //恢复文件
        FileOperations.reNameFile(processPath(nodeInfoPath));
        FileOperations.reNameFile(processPath(corpusPath));
        FileOperations.reNameFile(classifierModelPath+".bak");
        FileOperations.reNameFile(classifierTraindataPath+".bak");
        FileOperations.delete(processPath(gateJapePath));
        FileOperations.delete(processPath(gateGazetteerPath));
        //并删除更新出错的文件夹
        FileOperations.delete(nodeInfoPath);
        FileOperations.delete(corpusPath);
        FileOperations.delete(classifierModelPath);
        FileOperations.delete(classifierTraindataPath);
        FileOperations.delete(gateJapePath);
        FileOperations.delete(gateGazetteerPath);
    }


    /**
     * description: 删除备份文件corpus,nodeInfo,model.bin,traindata
     * @Param: null
     * @return null
     */
    public void deleteRecoveryFile() throws updateFileOperateException {
        FileOperations.delete(processPath(nodeInfoPath));
        FileOperations.delete(processPath(corpusPath));
        FileOperations.delete(classifierModelPath+".bak");
        FileOperations.delete(classifierTraindataPath+".bak");
        FileOperations.delete(processPath(gateJapePath));
        FileOperations.delete(processPath(gateGazetteerPath));
    }

    public String processPath(String path){
        String newPath = path.substring(0,path.length()-1)+"bak";
        return newPath;
    }

    public void test() throws Exception {
        parserPool.testCorpus();
    }

    public boolean updateProcess()throws Exception{
        return updateProcesses.updateProcessAction();
    }
}
